<!DOCTYPE html>
<html lang="zh-CN">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="robots" content="noodp" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1">
        <title>《FlutterPerformance分析工具简介》 - 飞雪无情的博客</title><meta name="Description" content="专注于IT互联网，包括但不限于Go语言(golang)、Java、Android、Python、项目管理、抖音分析、软件架构等"><meta property="og:title" content="《FlutterPerformance分析工具简介》" />
<meta property="og:description" content="
Flutter 提供了不少性能分析工具，但感觉相关文档不咋的，散落在官方的各个地方，对开发者不太友好。另外，有的工具目前只在 Android Studio 中有，比如 Flutter Performance；甚至有的工具只在 debug 模式下可用。本文对这些工具简单整理一下，便于做性能优化时查找和参考。
" />
<meta property="og:type" content="article" />
<meta property="og:url" content="https://www.flysnow.org/posts/flutter/FlutterPerformance%E5%88%86%E6%9E%90%E5%B7%A5%E5%85%B7%E7%AE%80%E4%BB%8B/" />
<meta property="article:published_time" content="2021-07-31T21:09:34+08:00" />
<meta property="article:modified_time" content="2021-07-31T21:09:34+08:00" />
<meta name="twitter:card" content="summary"/>
<meta name="twitter:title" content="《FlutterPerformance分析工具简介》"/>
<meta name="twitter:description" content="
Flutter 提供了不少性能分析工具，但感觉相关文档不咋的，散落在官方的各个地方，对开发者不太友好。另外，有的工具目前只在 Android Studio 中有，比如 Flutter Performance；甚至有的工具只在 debug 模式下可用。本文对这些工具简单整理一下，便于做性能优化时查找和参考。
"/>
<meta name="application-name" content="飞雪无情的博客">
<meta name="apple-mobile-web-app-title" content="飞雪无情的博客"><link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
        <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
        <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"><link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"><link rel="manifest" href="/site.webmanifest"><link rel="canonical" href="https://www.flysnow.org/posts/flutter/FlutterPerformance%E5%88%86%E6%9E%90%E5%B7%A5%E5%85%B7%E7%AE%80%E4%BB%8B/" /><link rel="prev" href="https://www.flysnow.org/posts/flutter/Flutter%E4%B9%8BStatefulWidget%E4%B8%8E%E6%80%A7%E8%83%BD/" /><link rel="next" href="https://www.flysnow.org/posts/flutter/Flutter-drawFrame/" /><link rel="stylesheet" href="/lib/normalize/normalize.min.css"><link rel="stylesheet" href="/css/style.min.css"><link rel="stylesheet" href="/lib/fontawesome-free/all.min.css"><link rel="stylesheet" href="/lib/animate/animate.min.css"><script type="application/ld+json">
    {
        "@context": "http://schema.org",
        "@type": "BlogPosting",
        "headline": "《FlutterPerformance分析工具简介》",
        "inLanguage": "zh-CN",
        "mainEntityOfPage": {
            "@type": "WebPage",
            "@id": "https:\/\/www.flysnow.org\/posts\/flutter\/FlutterPerformance%E5%88%86%E6%9E%90%E5%B7%A5%E5%85%B7%E7%AE%80%E4%BB%8B\/"
        },"genre": "posts","keywords": "flutter","wordcount":  3089 ,
        "url": "https:\/\/www.flysnow.org\/posts\/flutter\/FlutterPerformance%E5%88%86%E6%9E%90%E5%B7%A5%E5%85%B7%E7%AE%80%E4%BB%8B\/","datePublished": "2021-07-31T21:09:34+08:00","dateModified": "2021-07-31T21:09:34+08:00","publisher": {
            "@type": "Organization",
            "name": "飞雪无情"},"author": {
                "@type": "Person",
                "name": "飞雪无情"
            },"description": ""
    }
    </script></head>
    <body header-desktop="" header-mobile=""><script type="text/javascript">(window.localStorage && localStorage.getItem('theme') ? localStorage.getItem('theme') === 'dark' : ('' === 'auto' ? window.matchMedia('(prefers-color-scheme: dark)').matches : '' === 'dark')) && document.body.setAttribute('theme', 'dark');</script>

        <div id="mask"></div><div class="wrapper"><div class="logo-wrapper">
  <a href="/%20/" class="logo">飞雪无情的博客</a>
</div>

<nav class="site-navbar">
  <ul id="menu" class="menu">
    <li class="menu-item">
      <a class="menu-item-link" href="/tools/">工具</a>
    </li><li class="menu-item">
      <a class="menu-item-link" href="/archives/">归档</a>
    </li><li class="menu-item">
      <a class="menu-item-link" href="/about/">关于</a>
    </li>
  </ul>
</nav><main class="main">
                <div class="container"><div class="toc" id="toc-auto">
            <h2 class="toc-title">Contents</h2>
            <div class="toc-content" id="toc-content-auto"></div>
        </div><article class="page single"><h1 class="single-title animated flipInX">《FlutterPerformance分析工具简介》</h1><div class="post-meta">
            <div class="post-meta-line"><span class="post-author"><a href="/" title="Author" rel=" author" class="author"><i class="fas fa-user-circle fa-fw"></i>飞雪无情</a></span>&nbsp;<span class="post-category">included in <a href="/categories/Flutter/"><i class="far fa-folder fa-fw"></i>Flutter</a></span></div>
            <div class="post-meta-line"><i class="far fa-calendar-alt fa-fw"></i>&nbsp;<time datetime="2021-07-31">2021-07-31</time>&nbsp;<i class="fas fa-pencil-alt fa-fw"></i>&nbsp;3089 words&nbsp;
                <i class="far fa-clock fa-fw"></i>&nbsp;7 minutes&nbsp;</div>
        </div><div class="featured-image"><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="https://luckly007.oss-cn-beijing.aliyuncs.com/img/90c6cc12-742e-4c9f-b318-b912f163b8d0.png"
        data-srcset="https://luckly007.oss-cn-beijing.aliyuncs.com/img/90c6cc12-742e-4c9f-b318-b912f163b8d0.png, https://luckly007.oss-cn-beijing.aliyuncs.com/img/90c6cc12-742e-4c9f-b318-b912f163b8d0.png 1.5x, https://luckly007.oss-cn-beijing.aliyuncs.com/img/90c6cc12-742e-4c9f-b318-b912f163b8d0.png 2x"
        data-sizes="auto"
        alt="https://luckly007.oss-cn-beijing.aliyuncs.com/img/90c6cc12-742e-4c9f-b318-b912f163b8d0.png"
        title="https://luckly007.oss-cn-beijing.aliyuncs.com/img/90c6cc12-742e-4c9f-b318-b912f163b8d0.png" /></div><div class="details toc" id="toc-static"  kept="">
                <div class="details-summary toc-title">
                    <span>Contents</span>
                    <span><i class="details-icon fas fa-angle-right"></i></span>
                </div>
                <div class="details-content toc-content" id="toc-content-static"><nav id="TableOfContents">
  <ul>
    <li><a href="#帧渲染图表">帧渲染图表</a></li>
    <li><a href="#帧事件图表">帧事件图表</a></li>
    <li><a href="#cpu-分析器">CPU 分析器</a></li>
  </ul>

  <ul>
    <li><a href="#layers">layers</a></li>
    <li><a href="#layout">layout</a></li>
    <li><a href="#animation">animation</a></li>
    <li><a href="#performance">performance</a></li>
  </ul>
</nav></div>
            </div><div class="content" id="content"><blockquote>
<p>Flutter 提供了不少性能分析工具，但感觉相关文档不咋的，散落在官方的各个地方，对开发者不太友好。另外，有的工具目前只在 Android Studio 中有，比如 Flutter Performance；甚至有的工具只在 debug 模式下可用。本文对这些工具简单整理一下，便于做性能优化时查找和参考。</p>
</blockquote>
<h1 id="performance-overlay">Performance Overlay</h1>
<p>开启 Performance Overlay 有多种方式，最简单的几种包括：</p>
<ul>
<li>Android Studio &gt; Flutter Inspector &gt; Performance Overlay</li>
<li>Android Studio &gt; Flutter Performance &gt; Performance Overlay</li>
<li>DevTools &gt; Timeline &gt; Performance Overlay</li>
</ul>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="https://www.sunmoonblog.com/images/15786503018684.jpg"
        data-srcset="https://www.sunmoonblog.com/images/15786503018684.jpg, https://www.sunmoonblog.com/images/15786503018684.jpg 1.5x, https://www.sunmoonblog.com/images/15786503018684.jpg 2x"
        data-sizes="auto"
        alt="https://www.sunmoonblog.com/images/15786503018684.jpg"
        title="-w477" /></p>
<p>性能图层用两张图表显示应用的耗时信息。每一张图表都代表当前线程的最近 300 帧表现。</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="https://www.sunmoonblog.com/images/15786490815418.jpg"
        data-srcset="https://www.sunmoonblog.com/images/15786490815418.jpg, https://www.sunmoonblog.com/images/15786490815418.jpg 1.5x, https://www.sunmoonblog.com/images/15786490815418.jpg 2x"
        data-sizes="auto"
        alt="https://www.sunmoonblog.com/images/15786490815418.jpg"
        title="img" /></p>
<ul>
<li>顶部的图形表示 GPU 线程所花费的时间</li>
<li>底部的图表显示了 UI 线程所花费的时间</li>
<li>竖轴表示耗时，沿竖轴的黑线是时间线 (间隔单位为 16ms)</li>
<li>横轴则表示帧，垂直的绿色条代表的是当前帧</li>
<li>卡顿时绿色条会变成红色条
<ul>
<li>如果是在 UI 图表出现了红色竖条，则表明 Dart 代码消耗了大量资源</li>
<li>而如果红色竖条是在 GPU 图表出现的，意味着场景太复杂导致无法快速渲染</li>
</ul>
</li>
</ul>
<p>更多信息请参考 <a href="https://flutter.dev/docs/perf/rendering/ui-performance#flutters-threads" target="_blank" rel="noopener noreffer">Flutter 线程</a></p>
<p>使用 Performance Overlay 可以对 UI 性能问题进行<strong>定性分析</strong>，大致判断到底是 Dart 代码执行过慢(布局慢)还是场景复杂无法快速渲染(渲染慢)。</p>
<p>Performance Overlay 可用于 profile 模式。</p>
<h1 id="performanceoverlay控件httpsapiflutterdevflutterwidgetsperformanceoverlay-classhtml"><a href="https://api.flutter.dev/flutter/widgets/PerformanceOverlay-class.html" target="_blank" rel="noopener noreffer">PerformanceOverlay控件</a></h1>
<p><a href="https://api.flutter.dev/flutter/widgets/PerformanceOverlay-class.html" target="_blank" rel="noopener noreffer">PerformanceOverlay</a> 可以视为 Performance Overlay 的高级版本(Performance Overlay 正是基于 PerformanceOverlay 控件来实现的)。</p>
<p>通过如下方式在代码中开启 <code>PerformanceOverlay</code> 控件。<a href="https://flutter.dev/docs/testing/code-debugging#performance-overlay" target="_blank" rel="noopener noreffer">参考</a>。</p>
<pre><code>class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      showPerformanceOverlay: true,
      title: 'My Awesome App',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'My Awesome App'),
    );
  }
}
</code></pre><p>如果没有使用 <code>MaterialApp</code> 等控件，可以自己调用 <code>PerformanceOverlay.allEnabled(checkerboardOffscreenLayers: true,);</code> 来实现类似效果。</p>
<h1 id="observatoryhttpsdart-langgithubioobservatory-之-timeline"><a href="https://dart-lang.github.io/observatory/" target="_blank" rel="noopener noreffer">Observatory</a> 之 Timeline</h1>
<p>Observatory 是 Dart SDK 提供的用于分析和调试 Dart 程序的工具。可以使用 Observatory 来观察 Dart VM 内部状态，获取应用的实时数据。主要功能包括：</p>
<ul>
<li>检查应用的耗时情况</li>
<li>检查已分配内存</li>
<li>检查哪些代码已执行</li>
<li>调试内存泄漏问题</li>
<li>调试内存碎片问题</li>
</ul>
<p>虽然 Observatory 正在被 DevTools 慢慢取代，但某些情况下它提供的性能数据更为详细。</p>
<p>Observatory 的使用步骤：</p>
<ol>
<li>VS Code 或命令行下打开 Observatory</li>
<li>点击 timeline</li>
<li>点击 Flutter Developer</li>
<li>操作应用</li>
<li>点击 Refresh 加载数据</li>
<li>w/s 放大， a/d (点?号可以看到全部的快捷键)</li>
</ol>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="https://www.sunmoonblog.com/images/15813940478063.jpg"
        data-srcset="https://www.sunmoonblog.com/images/15813940478063.jpg, https://www.sunmoonblog.com/images/15813940478063.jpg 1.5x, https://www.sunmoonblog.com/images/15813940478063.jpg 2x"
        data-sizes="auto"
        alt="https://www.sunmoonblog.com/images/15813940478063.jpg"
        title="img" /></p>
<p>刷新后可以在事件面板中检查和分析UI线程和GPU的耗时，以定位性能瓶颈。</p>
<ul>
<li>将 <code>debugProfileBuildsEnabled</code> 设置为 <code>true</code> 可以在 Observatory 中看到 构建 Widget 的耗时</li>
<li>Save 后导出的 json 文件可以重新导入 Chrome 进行分析。Chrome 中输入 <code>chrome://tracing</code> 然后将 json 文件拖进去</li>
</ul>
<h1 id="devtools-之-timeline">DevTools 之 Timeline</h1>
<p><a href="https://flutter.dev/docs/development/tools/devtools/timeline#frame-rendering-chart" target="_blank" rel="noopener noreffer">时间线视图</a>用于显示 Flutter 帧信息。它由三个部分组成。</p>
<ul>
<li>帧渲染图表</li>
<li>帧事件图表</li>
<li>CPU 分析器</li>
</ul>
<p>使用 Timeline 可以对 UI 性能问题进行<strong>定量分析</strong>，通过观察布局阶段和渲染阶段的耗时，进一步定位问题原因。</p>
<p>Performance Overlay 可用于 profile 模式。</p>
<h2 id="帧渲染图表">帧渲染图表</h2>
<p>帧渲染图表跟 Performance Overlay 很类似。</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="https://www.sunmoonblog.com/images/15786578717563.jpg"
        data-srcset="https://www.sunmoonblog.com/images/15786578717563.jpg, https://www.sunmoonblog.com/images/15786578717563.jpg 1.5x, https://www.sunmoonblog.com/images/15786578717563.jpg 2x"
        data-sizes="auto"
        alt="https://www.sunmoonblog.com/images/15786578717563.jpg"
        title="img" /></p>
<p>图表中的每个条形框都代表一帧，每帧中不同线程的执行情况以不同颜色表示。</p>
<ul>
<li>UI 线程执行 Dart VM 中的 Dart 代码。构建过于复杂的图层树可能导致这个线程变慢</li>
<li>GPU 线程执行 Flutter 引擎中图形相关的代码。某些图层树易于构建却难于渲染，也可能导致这个线程变慢</li>
</ul>
<h2 id="帧事件图表">帧事件图表</h2>
<p>在帧渲染图表中选中一帧后，帧事件图表中将显示具体事件及其耗时。</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="https://www.sunmoonblog.com/images/15786583559211.jpg"
        data-srcset="https://www.sunmoonblog.com/images/15786583559211.jpg, https://www.sunmoonblog.com/images/15786583559211.jpg 1.5x, https://www.sunmoonblog.com/images/15786583559211.jpg 2x"
        data-sizes="auto"
        alt="https://www.sunmoonblog.com/images/15786583559211.jpg"
        title="-w1274" /></p>
<ul>
<li><a href="https://api.flutter.dev/flutter/widgets/debugProfileBuildsEnabled.html" target="_blank" rel="noopener noreffer">debugProfileBuildsEnabled</a> - 向 Timeline 事件中添加 build 信息</li>
<li><a href="https://api.flutter.dev/flutter/rendering/debugProfilePaintsEnabled.html" target="_blank" rel="noopener noreffer">debugProfilePaintsEnabled</a> - 向 timeline 事件中添加 paint 信息</li>
<li><a href="https://api.flutter.dev/flutter/widgets/debugPrintRebuildDirtyWidgets.html" target="_blank" rel="noopener noreffer">debugPrintRebuildDirtyWidgets</a> - 记录每帧重建的 widget</li>
</ul>
<h2 id="cpu-分析器">CPU 分析器</h2>
<ul>
<li>采样率
<ul>
<li>高采样率 - 单元时间内采集的 CPU 信息会更加详细且采集样例更多。但应用性能可能下降，采样存储空间可能溢出</li>
<li>单元时间内采集的 CPU 信息会比较粗略且采集样例较少。对应用性能影响小，可以采集更长时间</li>
</ul>
</li>
<li>图表
<ul>
<li>火焰图 - 展示的是自上而下的调用堆栈信息，即上面的堆栈帧调用下面的堆栈帧。每一个堆<strong>栈帧的宽度代表 CPU 执行的时长</strong>。调用堆栈栈帧消耗 CPU 的时间越长，就越洽有可能是我们进行性能改进的好地方</li>
<li>调用树 - 展示的是自上而下展示 CPU 中的调用堆栈信息</li>
<li>Bottom up 视图 - 用于显示方法调用堆栈，是一个自下而上的表示方式。表格中的每个最上方的方法实际上是给定 CPU 样本的调用堆栈中的最后一个方法</li>
</ul>
</li>
</ul>
<h1 id="chrome-tracing">Chrome Tracing</h1>
<p>我们可以从 DevTools Timeline 中导出 timeline 文件。</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="https://www.sunmoonblog.com/images/15786557953993.jpg"
        data-srcset="https://www.sunmoonblog.com/images/15786557953993.jpg, https://www.sunmoonblog.com/images/15786557953993.jpg 1.5x, https://www.sunmoonblog.com/images/15786557953993.jpg 2x"
        data-sizes="auto"
        alt="https://www.sunmoonblog.com/images/15786557953993.jpg"
        title="-w1000" /></p>
<p>导出的 timeline 文件可以重新导入到 chrome tracing 进行分析。</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="https://www.sunmoonblog.com/images/15786559387247.jpg"
        data-srcset="https://www.sunmoonblog.com/images/15786559387247.jpg, https://www.sunmoonblog.com/images/15786559387247.jpg 1.5x, https://www.sunmoonblog.com/images/15786559387247.jpg 2x"
        data-sizes="auto"
        alt="https://www.sunmoonblog.com/images/15786559387247.jpg"
        title="-w1000" /></p>
<p>chrome tracing 几个常用的操作键包括：</p>
<ul>
<li>W - 放大</li>
<li>S - 缩小</li>
<li>A - 左移</li>
<li>D - 右移</li>
</ul>
<p>chrome tracing 工具展示如下两个阶段的耗时：</p>
<ul>
<li>VSYNC</li>
<li>GPURasterizer:Draw</li>
</ul>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="https://www.sunmoonblog.com/images/15786563521810.jpg"
        data-srcset="https://www.sunmoonblog.com/images/15786563521810.jpg, https://www.sunmoonblog.com/images/15786563521810.jpg 1.5x, https://www.sunmoonblog.com/images/15786563521810.jpg 2x"
        data-sizes="auto"
        alt="https://www.sunmoonblog.com/images/15786563521810.jpg"
        title="-w1147" /></p>
<p>(TODO 如何解读这些数据? 比 CPU 分析器更容易观察数据?)</p>
<p><a href="https://api.flutter.dev/flutter/dart-developer/Timeline-class.html" target="_blank" rel="noopener noreffer">Timeline</a> 用于向时间线添加同步事件。使用方法很简单：</p>
<pre><code>Timeline.startSync(&quot;Doing Something&quot;);
doSomething();
Timeline.finishSync();
</code></pre><p>或者：</p>
<pre><code>Timeline.timeSync(&quot;Doing Something&quot;, () {
  doSomething();
});
</code></pre><p>我们在代码中添加 “Doing Something”，</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="https://www.sunmoonblog.com/images/15790537579215.jpg"
        data-srcset="https://www.sunmoonblog.com/images/15790537579215.jpg, https://www.sunmoonblog.com/images/15790537579215.jpg 1.5x, https://www.sunmoonblog.com/images/15790537579215.jpg 2x"
        data-sizes="auto"
        alt="https://www.sunmoonblog.com/images/15790537579215.jpg"
        title="-w519" /></p>
<p>然后将 DevTools Timeline 导出的 timeline 文件重新导入到 Chrome Tracing 工具。从中我们可以找到 “Doing Something” 对应的事件，如下图：</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="https://www.sunmoonblog.com/images/15790539789634.jpg"
        data-srcset="https://www.sunmoonblog.com/images/15790539789634.jpg, https://www.sunmoonblog.com/images/15790539789634.jpg 1.5x, https://www.sunmoonblog.com/images/15790539789634.jpg 2x"
        data-sizes="auto"
        alt="https://www.sunmoonblog.com/images/15790539789634.jpg"
        title="-w900" /></p>
<h1 id="widget-rebuild-profilerhttpsfluttercndocsdevelopmenttoolsandroid-studioshow-performance-data"><a href="https://flutter.cn/docs/development/tools/android-studio#show-performance-data" target="_blank" rel="noopener noreffer">Widget rebuild profiler</a></h1>
<p>Android Studio 中 View &gt; Tool Windows &gt; Flutter Performance 打开性能工具窗口，在 Widget rebuild stats 中勾选 Track widget rebuilds 来查看 widget 的重建信息。重建信息包括 Widget 名字、源码位置、上一帧中重建次数、当前页面中重建次数。此外，Widget 名字前面还会显示一个小图标。</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="https://www.sunmoonblog.com/images/15788975395660.jpg"
        data-srcset="https://www.sunmoonblog.com/images/15788975395660.jpg, https://www.sunmoonblog.com/images/15788975395660.jpg 1.5x, https://www.sunmoonblog.com/images/15788975395660.jpg 2x"
        data-sizes="auto"
        alt="https://www.sunmoonblog.com/images/15788975395660.jpg"
        title="-w697" /></p>
<ul>
<li>黄色旋转圆圈 - 重建次数过多</li>
<li>灰色圆圈 - 未重建</li>
<li>灰色旋转圆圈 其他情况</li>
</ul>
<p>这个功能的目的是让你了解 widget 是何时重建的，如果发生不符合预期的重建，就需要优化代码了。优化方法请参考 <a href="https://api.flutter.dev/flutter/widgets/StatefulWidget-class.html#performance-considerations" target="_blank" rel="noopener noreffer">StatefulWidget 与性能</a> (译文见<a href="https://www.sunmoonblog.com/2020/01/10/stateful-widget-performance-considerations/" target="_blank" rel="noopener noreffer">这里</a>)</p>
<p>Widget rebuild profiler 只能用于 debug 模式。</p>
<h1 id="fps">FPS</h1>
<p>Performance Overlay 使用两张图来表示 UI线程 和 GPU线程的最近 300 帧表现。</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="https://www.sunmoonblog.com/images/15786490815418.jpg"
        data-srcset="https://www.sunmoonblog.com/images/15786490815418.jpg, https://www.sunmoonblog.com/images/15786490815418.jpg 1.5x, https://www.sunmoonblog.com/images/15786490815418.jpg 2x"
        data-sizes="auto"
        alt="https://www.sunmoonblog.com/images/15786490815418.jpg"
        title="img" /></p>
<p>不过 FPS 对我们来说更直接。Android Studio Flutter Performance 中可以看到 FPS 值：</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="https://www.sunmoonblog.com/images/15788855830332.jpg"
        data-srcset="https://www.sunmoonblog.com/images/15788855830332.jpg, https://www.sunmoonblog.com/images/15788855830332.jpg 1.5x, https://www.sunmoonblog.com/images/15788855830332.jpg 2x"
        data-sizes="auto"
        alt="https://www.sunmoonblog.com/images/15788855830332.jpg"
        title="-w703" /></p>
<p>我们也可以使用 <code>WidgetsBinding.addTimingsCallback</code> 方法来自行统计 FPS。这里提供两个参考实现：</p>
<ul>
<li><a href="https://gist.github.com/yrom/ac4f30b26ee02ce3bd3a1d260bb9ffb4" target="_blank" rel="noopener noreffer">统计FPS</a></li>
<li><a href="https://gist.github.com/410063005/3293c1faa78a01a3d0e7b4e2ce12d1af" target="_blank" rel="noopener noreffer">FpsWidget</a></li>
</ul>
<h1 id="测试参数httpsflutterdevdocstestingcode-debugging"><a href="https://flutter.dev/docs/testing/code-debugging" target="_blank" rel="noopener noreffer">测试参数</a></h1>
<p>刚入门 Flutter 开发时，你肯定会如何右上角那个刺眼的 debug 标志苦恼过。</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="https://www.sunmoonblog.com/images/15789021349152.jpg"
        data-srcset="https://www.sunmoonblog.com/images/15789021349152.jpg, https://www.sunmoonblog.com/images/15789021349152.jpg 1.5x, https://www.sunmoonblog.com/images/15789021349152.jpg 2x"
        data-sizes="auto"
        alt="https://www.sunmoonblog.com/images/15789021349152.jpg"
        title="Debug logo -w320" /></p>
<p>方法很简单。将 <code>debugShowCheckedModeBanner</code> 参数设置为 <code>false</code> 即可。</p>
<pre><code>MaterialApp(
 debugShowCheckedModeBanner: false
)
</code></pre><p>Flutter 提供性能测试/调试参数，这些参数通常是布尔类型，只要在代码中打开相应的参数就可以开始进行性能测试。</p>
<ul>
<li><a href="https://api.flutter-io.cn/flutter/rendering/PerformanceOverlayLayer/checkerboardOffscreenLayers.html" target="_blank" rel="noopener noreffer">PerformanceOverlayLayer.checkerboardOffscreenLayers</a> 用于检查是否使用 <code>saveLayer</code>，详情请参考 <a href="https://flutter.dev/docs/perf/rendering/ui-performance#checking-for-offscreen-layers" target="_blank" rel="noopener noreffer">Checking for offscreen layers</a></li>
<li><a href="https://api.flutter-io.cn/flutter/rendering/PerformanceOverlayLayer/checkerboardRasterCacheImages.html" target="_blank" rel="noopener noreffer">PerformanceOverlayLayer.checkerboardRasterCacheImages</a> 用于检查哪些图片被缓存，请情请参考 <a href="https://flutter.dev/docs/perf/rendering/ui-performance#checking-for-non-cached-images" target="_blank" rel="noopener noreffer">Checking for non-cached images</a></li>
</ul>
<pre><code>MaterialApp(
  checkerboardOffscreenLayers: true,
  checkerboardRasterCacheImages: true,
)
</code></pre><h1 id="debug-flags">Debug flags</h1>
<h2 id="layers">layers</h2>
<p>Flutter framework 的每一层都提供了函数来输出当前状态或事件到控制台(这些函数调用 <code>debugPrint</code>)。</p>
<p>调用 <a href="https://api.flutter-io.cn/flutter/widgets/debugDumpApp.html" target="_blank" rel="noopener noreffer">debugDumpApp()</a> 输出 the state of the Widgets library。不应在 <code>build()</code> 方法中调用 <code>debugDumpApp()</code>，可以在调用 <code>runApp()</code> 后的任何时候调用 <code>debugDumpApp()</code>。调用 <a href="https://api.flutter-io.cn/flutter/widgets/Widget/debugFillProperties.html" target="_blank" rel="noopener noreffer">debugFillProperties()</a> 方法向 <code>debugDumpApp()</code> 的输出添加自定义信息。</p>
<p>调用 <a href="https://api.flutter-io.cn/flutter/rendering/debugDumpRenderTree.html" target="_blank" rel="noopener noreffer">debugDumpRenderTree()</a> 方法来输出 rendering tree，用于分析布局问题。建议在 <a href="https://api.flutter-io.cn/flutter/scheduler/SchedulerBinding/addPersistentFrameCallback.html" target="_blank" rel="noopener noreffer">frame callback</a> 或 event handler 中调用 <code>debugDumpRenderTree()</code> 方法。分析布局问题时，关注的关键字段是 <code>size</code> 和 <code>constraints</code>。调用 <a href="https://api.flutter-io.cn/flutter/rendering/Layer/debugFillProperties.html" target="_blank" rel="noopener noreffer">debugFillProperties()</a> 方法向 <code>debugDumpRenderTree()</code> 的输出添加自定义信息。</p>
<p>调用 <a href="https://api.flutter-io.cn/flutter/rendering/debugDumpLayerTree.html" target="_blank" rel="noopener noreffer">debugDumpLayerTree()</a> 方法来输出 layer tree，用于分析 compositing 问题。</p>
<blockquote>
<p>The RepaintBoundary widget, which creates a RenderRepaintBoundary in the render tree, creates a new layer in the layer tree. This is used to reduce how much needs to be repainted.</p>
</blockquote>
<p>可以使用 <code>RepaintBoundary</code> 来在 render tree 中创建 <code>RenderRepaintBoundary</code> ，即，在 layer tree 中创建新的 layer。这种方法可以减少重绘工作。详细可参考 <a href="https://juejin.im/post/5deda528f265da339f7ce93c" target="_blank" rel="noopener noreffer">说说Flutter中的RepaintBoundary - 掘金</a>。</p>
<p>调用 <a href="https://api.flutter.dev/flutter/rendering/debugDumpSemanticsTree.html" target="_blank" rel="noopener noreffer">debugDumpSemanticsTree()</a> 方法来输出 semantics tree (这棵树表示 system accessibility APIs)。暂时用不上，略过。</p>
<p>调用 <a href="https://api.flutter.dev/flutter/scheduler/debugPrintBeginFrameBanner.html" target="_blank" rel="noopener noreffer">debugPrintBeginFrameBanner</a> 和 <a href="https://api.flutter.dev/flutter/scheduler/debugPrintEndFrameBanner.html" target="_blank" rel="noopener noreffer">debugPrintEndFrameBanner</a> 方法来输出一帧的开始和结束。</p>
<pre><code>void main() {
  runApp(DemoApp());
  debugPrintBeginFrameBanner = true;
  debugPrintEndFrameBanner = true;
}
</code></pre><h2 id="layout">layout</h2>
<p><a href="https://api.flutter.dev/flutter/rendering/debugPaintSizeEnabled.html" target="_blank" rel="noopener noreffer">debugPaintSizeEnabled</a> 用于调试布局问题。其用法如下：</p>
<pre><code>void main() {
    debugPaintSizeEnabled = true;
    runApp(DemoApp());
}
</code></pre><ul>
<li>box - 所有的 box 外都显示一个亮蓝绿色边界</li>
<li>padding - 以浅蓝色显示 <code>Padding</code> 等控件 (带一个深蓝色 box)</li>
<li>alignment - 包括 <code>Center</code> 和 <code>Align</code> 等控件，以黄色箭头显示</li>
<li>spacer - 以灰色显示，比如没有子节点的 <code>Container</code> 就属于这一类</li>
</ul>
<p><a href="https://api.flutter.dev/flutter/rendering/debugPaintBaselinesEnabled.html" target="_blank" rel="noopener noreffer">debugPaintBaselinesEnabled</a> 的作用类似，它显示的是对象的基线。</p>
<p><a href="https://api.flutter.dev/flutter/rendering/debugPaintPointersEnabled.html" target="_blank" rel="noopener noreffer">debugPaintPointersEnabled</a> 用于开启一个特殊的模式：该模式下被点击的对象以蓝绿色显示。这个功能用于检查 hit test 是否正确。</p>
<p>还有几个用于调试 compositor layer 的 flag，</p>
<ul>
<li><a href="https://api.flutter.dev/flutter/rendering/debugPaintLayerBordersEnabled.html" target="_blank" rel="noopener noreffer">debugPaintLayerBordersEnabled</a> - 以橙色显示每个 layer 的边界，用于检查是否需要使用 <code>RepaintBoundary</code> 控件</li>
<li><a href="https://api.flutter.dev/flutter/rendering/debugRepaintRainbowEnabled.html" target="_blank" rel="noopener noreffer">debugRepaintRainbowEnabled</a> - 每个 layer 重绘时会叠加上不同的颜色。使用技巧：如果一个静态的 widget 颜色出现变化，可以考虑给它加上 <code>RepaintBoundary</code></li>
<li>debugPrintScheduleBuildForStacks - 为什么被构建</li>
<li>debugPrintRebuildDirtyWidgets - 什么组件被重新构建了</li>
<li>debugProfileBuildsEnabled - 在观测台里显示构建树</li>
<li><a href="https://api.flutter.dev/flutter/rendering/debugPaintLayerBordersEnabled.html" target="_blank" rel="noopener noreffer">debugPaintLayerBordersEnabled</a> - 查看 layer 界线</li>
<li><a href="https://api.flutter.dev/flutter/widgets/debugProfileBuildsEnabled.html" target="_blank" rel="noopener noreffer">debugProfileBuildsEnabled</a> - Adds Timeline events for every Widget built.</li>
<li><a href="https://api.flutter.dev/flutter/rendering/debugProfilePaintsEnabled.html" target="_blank" rel="noopener noreffer">debugProfilePaintsEnabled</a></li>
<li>debugPrintRebuildDirtyWidgets
<ul>
<li>结合 <a href="https://api.flutter.dev/flutter/widgets/debugPrintScheduleBuildForStacks.html" target="_blank" rel="noopener noreffer">debugPrintScheduleBuildForStacks</a>，可以观察 widget 的 dirty/clean 生命周期</li>
<li>结合 <a href="https://api.flutter.dev/flutter/widgets/debugProfileBuildsEnabled.html" target="_blank" rel="noopener noreffer">debugProfileBuildsEnabled</a>，可以在 DevTools Timeline 中观察到详细事件信息</li>
</ul>
</li>
</ul>
<p>注意：所有这些以 debug 开头的 flag 均只能工作于 debug 模式。</p>
<h2 id="animation">animation</h2>
<p>调试动画的最有效方式是减慢其速度。可以使用 DevTool 中 <a href="https://flutter.dev/docs/development/tools/devtools/inspector" target="_blank" rel="noopener noreffer">Inspector view</a> 的 Slow Animations 按钮来减慢。可以使用 <a href="https://api.flutter.dev/flutter/scheduler/timeDilation.html" target="_blank" rel="noopener noreffer">timeDilation</a> 来更精确地控制动画速度。</p>
<h2 id="performance">performance</h2>
<p><a href="https://api.flutter.dev/flutter/rendering/debugPrintMarkNeedsLayoutStacks.html" target="_blank" rel="noopener noreffer">debugPrintMarkNeedsLayoutStacks</a> - 如果 layout 次数比预期中的要多，可以通过这个 flag 来观察 render box 被置为 dirty 的原因。</p>
<h1 id="flutter-driver">Flutter Driver</h1>
<p>TODO</p>
<ul>
<li><a href="https://flutter.cn/docs/perf/rendering/ui-performance#benchmarking">https://flutter.cn/docs/perf/rendering/ui-performance#benchmarking</a></li>
</ul>
<h1 id="一些小技巧">一些小技巧</h1>
<ul>
<li>检查 Performance Overlay</li>
<li>检查 FPS (自行计算 FPS)</li>
<li>检查屏幕之外的视图 (使用 <code>checkerboardOffscreenLayers</code>)</li>
<li>检查没有缓存的图像 (使用 <code>checkerboardRasterCacheImages</code>)</li>
<li>检查 widget 重建性能 (使用 Widget rebuild stats)</li>
</ul>
<p>优化点：</p>
<ul>
<li>想办法减少 widget 重建</li>
</ul>
<h1 id="参考">参考</h1>
<ul>
<li><a href="https://flutter.dev/docs/testing/code-debugging" target="_blank" rel="noopener noreffer">code-debugging</a></li>
<li><a href="https://flutter.dev/docs/testing/debugging" target="_blank" rel="noopener noreffer">debugging</a></li>
<li><a href="https://flutter.dev/docs/perf/rendering/ui-performance" target="_blank" rel="noopener noreffer">ui-performance</a></li>
<li><a href="https://medium.com/flutter/performance-testing-of-flutter-apps-df7669bb7df7" target="_blank" rel="noopener noreffer">Performance testing of Flutter apps - Flutter - Medium</a></li>
</ul></div><div class="post-footer" id="post-footer">
    <div class="post-info">
        <div class="post-info-line">
            <div class="post-info-mod">
                <span>Updated on 2021-07-31</span>
            </div>
            <div class="post-info-license"></div>
        </div>
        <div class="post-info-line">
            <div class="post-info-md"></div>
            <div class="post-info-share">
                <span></span>
            </div>
        </div>
    </div>

    <div class="post-info-more">
        <section class="post-tags"><i class="fas fa-tags fa-fw"></i>&nbsp;<a href="/tags/flutter/">flutter</a></section>
        <section>
            <span><a href="javascript:void(0);" onclick="window.history.back();">Back</a></span>&nbsp;|&nbsp;<span><a href="/">Home</a></span>
        </section>
    </div>

    <div class="post-nav"><a href="/posts/flutter/Flutter%E4%B9%8BStatefulWidget%E4%B8%8E%E6%80%A7%E8%83%BD/" class="prev" rel="prev" title="《Flutter之StatefulWidget与性能》"><i class="fas fa-angle-left fa-fw"></i>《Flutter之StatefulWidget与性能》</a>
            <a href="/posts/flutter/Flutter-drawFrame/" class="next" rel="next" title="《Flutter DrawFrame（）》">《Flutter DrawFrame（）》<i class="fas fa-angle-right fa-fw"></i></a></div>
</div>
</article></div>
            </main><footer class="footer">
        <div class="footer-container"><div class="footer-line">Powered by <a href="https://gohugo.io/" target="_blank" rel="noopener noreffer" title="Hugo 0.79.1">Hugo</a> | Theme - <a href="https://github.com/dillonzq/LoveIt" target="_blank" rel="noopener noreffer" title="LoveIt 0.2.10"><i class="far fa-kiss-wink-heart fa-fw"></i> LoveIt</a>
                </div><div class="footer-line"><i class="far fa-copyright fa-fw"></i><span itemprop="copyrightYear">2021</span><span class="author" itemprop="copyrightHolder">&nbsp;<a href="/" target="_blank">飞雪无情</a></span></div>
        </div>
    </footer></div>

        <div id="fixed-buttons"><a href="#" id="back-to-top" class="fixed-button" title="Back to Top">
                <i class="fas fa-arrow-up fa-fw"></i>
            </a><a href="#" id="view-comments" class="fixed-button" title="View Comments">
                <i class="fas fa-comment fa-fw"></i>
            </a>
        </div><script type="text/javascript" src="/lib/smooth-scroll/smooth-scroll.min.js"></script><script type="text/javascript" src="/lib/lazysizes/lazysizes.min.js"></script><script type="text/javascript" src="/lib/clipboard/clipboard.min.js"></script><script type="text/javascript" src="/lib/sharer/sharer.min.js"></script><script type="text/javascript">window.config={"code":{"copyTitle":"Copy to clipboard","maxShownLines":10},"comment":{}};</script><script type="text/javascript" src="/js/theme.min.js"></script></body>
</html>
